home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994…tember: Reference Library / Dev.CD Sep 94.toast / Periodicals / develop / develop Issue 19 / develop 19 code / Color Picker Sample / Article Code.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-07-21  |  14.5 KB  |  589 lines  |  [TEXT/KAHL]

  1. #include "string.h"
  2. #include "AppInterface.h"
  3.  
  4. #include "CMApplication.h"
  5. #include "ColorPicker.h"
  6.  
  7. ////////////////////////////////////////////////////////
  8. // Defines
  9.  
  10. // Edit menu constants redefined for the article code
  11. #define kMyEditMenuID    kEditMenuID
  12. #define    kMyUndoItem        iUndo
  13. #define    kMyCutItem        iCut
  14. #define    kMyCopyItem        iCopy
  15. #define    kMyPasteItem    iPaste
  16. #define    kMyClearItem    iClear
  17.  
  18. // ID of App dialog for a picker
  19. #define    kMyDialogID        129
  20.  
  21. // App dialog items
  22. #define kRevertButton    1
  23. #define kEditItem        2
  24. #define kStatText        3
  25. #define kMyLastItem        kStatText
  26.  
  27.  
  28. /////////////////////////////////////
  29. // Globals
  30.  
  31. RGBColor            myRGBColor = {65535, 32767, 0}; // A nice pukey orange.
  32. picker                myPicker = nil;        // Global picker reference.
  33. DialogPtr             myDialog = nil;
  34. WindowPtr             myDocWindow = nil;
  35. extern MenuHandle    gPickerMenuHandle;
  36.  
  37.     
  38. //////////////////////////////////
  39. // Prototypes
  40.  
  41. pascal Boolean MyEventProc(EventRecord *event);
  42. pascal void MyColorChangedProc(long userData, PMColorPtr newColor);
  43. pascal void MyModelessColorChangedProc(long userData, PMColorPtr newColor);
  44. void PickAColor(void);
  45. void DoNewColorStuff(PMColor *theColor);
  46. void DoTheUpdate(WindowPtr aWindow);
  47. OSErr BuildModelessSysDialog(void);
  48. OSErr BuildPickerDialog(void);
  49. OSErr BuildAppDialog(void);
  50. Boolean SampleDoEvent(EventRecord *event);
  51. void UseNewColor(picker thePicker);
  52. void UseOriginalColor(picker thePicker);
  53. void HandleAppItem(short itemHit);
  54. Boolean DoMenu(EventRecord *event);
  55. void HandleMenuChoice(long mChoice);
  56. void CloseAppPicker(void);
  57.  
  58. // Event-filter procedure and color-changed procedure
  59.  
  60. pascal Boolean MyEventProc(EventRecord *event) {
  61.     Boolean handled = false;    // Assume we don't handle the event.
  62.  
  63.     switch(event->what) {
  64.         case updateEvt:
  65.             // Check to see if the update is for our window.
  66.             if((WindowPtr) event->message == myDocWindow) {
  67.                 DoTheUpdate(myDocWindow);
  68.                 handled = true;
  69.             }
  70.     }
  71.     return handled;
  72. }
  73.  
  74. pascal void MyColorChangedProc(long userData, PMColorPtr newColor) {
  75.     GrafPtr port;
  76.     CWorld    cWorld;
  77.     CMColor color;
  78.     CMError cwError;
  79.     
  80.     GetPort(&port);
  81.     SetPort(myDocWindow);
  82.  
  83.     // If the color has a profile, convert it to RGB space.
  84.     if(newColor->profile) {
  85.         // Create a color world and convert the color. This color world
  86.         // matches from the color's space to the system space (RGB).
  87.         cwError = CWNewColorWorld(&cWorld, newColor->profile, 0L);
  88.         if(cwError == noErr || cwError == CMProfilesIdentical) {
  89.             // We created the color world. Now match the color using a copy
  90.             // so that we don't munge the original.
  91.             color = newColor->color;
  92.             CWMatchColors(cWorld, &color, 1);
  93.             CWDisposeColorWorld(cWorld);
  94.         }
  95.     } else
  96.         color.rgb = newColor->color.rgb;
  97.     
  98.     // Set our global color storage and paint the port with the new color.
  99.     myRGBColor = color.rgb;
  100.     RGBForeColor(&color.rgb);
  101.     PaintRect(&myDocWindow->portRect);
  102.  
  103.     SetPort(port);
  104. }
  105.  
  106. void PickAColor(void)
  107. {
  108.     // Calling PickColor
  109.         
  110.     ColorPickerInfo        cpInfo;
  111.     PMColor                savedColor;
  112.     
  113.     // Set the input color to be an RGB color in system space.
  114.     cpInfo.theColor.color.rgb = myRGBColor;
  115.     cpInfo.theColor.profile = 0L;
  116.     
  117.     // We're not picking for a destination profile.
  118.     cpInfo.dstProfile = 0L;
  119.     
  120.     cpInfo.flags = AppIsColorSyncAware | CanModifyPalette | CanAnimatePalette;
  121.     
  122.     // Center the picker on the deepest color screen (we don't need to worry
  123.     // about the dialogOrigin field in this case).
  124.     cpInfo.placeWhere = kDeepestColorScreen;
  125.     
  126.     // Use the default picker.
  127.     cpInfo.pickerType = 0L;
  128.     
  129.     // Install the callbacks.
  130.     cpInfo.eventProc = MyEventProc;
  131.     cpInfo.colorProc = MyColorChangedProc;
  132.     cpInfo.colorProcData = 0L;
  133.     
  134.     strcpy((char *)cpInfo.prompt,(char *)"\pChoose a new color");
  135.     
  136.     // Tell the Color Picker Manager about the Edit menu.
  137.     cpInfo.mInfo.editMenuID = kMyEditMenuID;
  138.     cpInfo.mInfo.cutItem = kMyCutItem;
  139.     cpInfo.mInfo.copyItem = kMyCopyItem;
  140.     cpInfo.mInfo.pasteItem = kMyPasteItem;
  141.     cpInfo.mInfo.clearItem = kMyClearItem;
  142.     cpInfo.mInfo.undoItem = kMyUndoItem;
  143.     
  144.     // Save the current color, in case the user cancels
  145.     savedColor = cpInfo.theColor;
  146.     
  147.     // And finally, pick that color!
  148.     if(PickColor(&cpInfo) == noErr && cpInfo.newColorChosen)
  149.         // Go use this new color. Remember it can be in any color space.
  150.         DoNewColorStuff(&cpInfo.theColor);
  151.     else
  152.         // Canceled or an error, restore old color
  153.         DoNewColorStuff(&savedColor);
  154. }
  155.  
  156. void DoNewColorStuff(PMColor *theColor)
  157. {
  158.     if(theColor->profile) 
  159.         SysBeep(10);        // exercise for the reader <snicker>
  160.     else
  161.     {
  162.         SetPort(myDocWindow);
  163.         myRGBColor = theColor->color.rgb;
  164.         RGBForeColor(&myRGBColor);
  165.         PaintRect(&myDocWindow->portRect);
  166.     }
  167. }
  168.  
  169. void DoTheUpdate(WindowPtr aWindow)
  170. {
  171.     BeginUpdate(aWindow);
  172.     SetPort(aWindow);
  173.     RGBForeColor(&myRGBColor);
  174.     PaintRect(&aWindow->portRect);
  175.     EndUpdate(aWindow);
  176. }
  177.  
  178. ////////////////////////////////////////////////////////////////////////////////
  179.  
  180.  
  181. OSErr BuildModelessSysDialog(void)
  182. {
  183.     // A modeless system-owned color picker dialog
  184.     
  185.     SystemDialogInfo     sInfo;
  186.     OSErr                result;
  187.     
  188.     sInfo.flags = DialogIsMoveable + AppIsColorSyncAware + CanModifyPalette
  189.                     + CanAnimatePalette;
  190.     sInfo.pickerType = 0L;
  191.     sInfo.placeWhere = kDeepestColorScreen;
  192.     sInfo.mInfo.editMenuID = kMyEditMenuID;
  193.     sInfo.mInfo.cutItem = kMyCutItem;
  194.     sInfo.mInfo.copyItem = kMyCopyItem;
  195.     sInfo.mInfo.pasteItem = kMyPasteItem;
  196.     sInfo.mInfo.clearItem = kMyClearItem;
  197.     sInfo.mInfo.undoItem = kMyUndoItem;
  198.     
  199.     myPicker = nil; // not in listing!
  200.     
  201.     result = CreateColorDialog(&sInfo, &myPicker);
  202.     return result;
  203. }
  204.  
  205. OSErr BuildAppDialog(void)
  206. {
  207.     ApplicationDialogInfo     aInfo;
  208.     OSErr                    result;
  209.     
  210.     // First create the dialog (make sure it's a color dialog so the color 
  211.     // picker can do all the color stuff it needs to do!).
  212.     myDialog = GetNewDialog(kMyDialogID, nil, (WindowPtr)-1);
  213.     
  214.     // Set up the ApplicationDialogInfo structure
  215.     aInfo.flags = DialogIsMoveable + AppIsColorSyncAware + 
  216.         CanModifyPalette + CanAnimatePalette;
  217.     aInfo.pickerType = 0L;
  218.     aInfo.theDialog = myDialog;
  219.     
  220.     // Put the color picker's origin at (0,0) in the dialog.
  221.     aInfo.pickerOrigin.h = 0;
  222.     aInfo.pickerOrigin.v = 0;
  223.     
  224.     // Set up edit menu info
  225.     aInfo.mInfo.editMenuID = kMyEditMenuID;
  226.     aInfo.mInfo.cutItem = kMyCutItem;
  227.     aInfo.mInfo.copyItem = kMyCopyItem;
  228.     aInfo.mInfo.pasteItem = kMyPasteItem;
  229.     aInfo.mInfo.clearItem = kMyClearItem;
  230.     aInfo.mInfo.undoItem = kMyUndoItem;
  231.     
  232.     // Finally, add the color picker to it.
  233.     result = AddPickerToDialog(&aInfo, &myPicker);
  234.     return result;
  235. }
  236.  
  237. OSErr BuildPickerDialog(void)
  238. {
  239.     PickerDialogInfo    pInfo;
  240.     OSErr                result;
  241.     
  242.     pInfo.flags = DialogIsMoveable + AppIsColorSyncAware + CanModifyPalette
  243.         + CanAnimatePalette;
  244.     pInfo.pickerType = 0L;
  245.     pInfo.mInfo.editMenuID = kMyEditMenuID;
  246.     pInfo.mInfo.cutItem = kMyCutItem;
  247.     pInfo.mInfo.copyItem = kMyCopyItem;
  248.     pInfo.mInfo.pasteItem = kMyPasteItem;
  249.     pInfo.mInfo.clearItem = kMyClearItem;
  250.     pInfo.mInfo.undoItem = kMyUndoItem;
  251.     
  252.     result = CreatePickerDialog(&pInfo, &myPicker);
  253.     return result;
  254. }
  255.  
  256. pascal void MyModelessColorChangedProc(long userData, PMColorPtr newColor) {
  257.     GrafPtr port;
  258.     CWorld    cWorld;
  259.     CMColor color;
  260.     CMError cwError;
  261.     
  262.     GetPort(&port);
  263.     SetPort(myDocWindow);
  264.  
  265.     // Now check to see if the color has a profile. If so, we need to
  266.     // convert it to RGB space.
  267.     if(newColor->profile) {
  268.         // Create a color world and convert the color. This color world
  269.         // matches from the color's space to the system space (RGB).
  270.         cwError = CWNewColorWorld(&cWorld, newColor->profile, 0L);
  271.         if(cwError == noErr || cwError == CMProfilesIdentical) {
  272.             // We created the color world. Now match the color using a copy
  273.             // so that we don't munge the original.
  274.             color = newColor->color;
  275.             CWMatchColors(cWorld, &color, 1);
  276.             CWDisposeColorWorld(cWorld);
  277.         }
  278.     } else
  279.         color.rgb = newColor->color.rgb;
  280.     
  281.     // Just paint the port with the new color.
  282.     RGBForeColor(&color.rgb);
  283.     PaintRect(&myDocWindow->portRect);
  284.  
  285.     SetPort(port);
  286. }
  287.  
  288.  ///////////////////////////////////////////////////////////////////
  289.  
  290. // Sample event loop
  291.  
  292. #define IsMenuKey(x)    ((x)->what == keyDown && (x)->modifiers & cmdKey)
  293.  
  294. Boolean SampleDoEvent(EventRecord *event) {
  295.     Boolean     handled = false, isMenuEvent = false;
  296.     EventData     pEvent;
  297.     short        inWhere;
  298.     WindowPtr    whichWindow;
  299.  
  300.      // We are assuming that the application always wants to handle menus.
  301.     if(event->what == mouseDown) {
  302.         inWhere = FindWindow(event->where, &whichWindow);
  303.         if(inWhere == inMenuBar)
  304.             isMenuEvent = true;
  305.     }
  306.      if(isMenuEvent || IsMenuKey(event)) {
  307.          DoMenu(event);
  308.          handled = true;
  309.     }
  310.         
  311.     // If the event's not handled yet, call the Color Picker Manager to
  312.     // give it a shot.
  313.     if(!handled && myPicker != nil) {
  314.         pEvent.event = event;
  315.         pEvent.colorProc = MyModelessColorChangedProc;
  316.         pEvent.colorProcData = 0L;
  317.         DoPickerEvent(myPicker, &pEvent);
  318.         handled = pEvent.handled;
  319.  
  320.         // If the color picker handled it, we might want to do something
  321.         // with the results.
  322.         if(handled)
  323.         {
  324.             switch(pEvent.action) {
  325.                 case kDidNothing:
  326.                     break;
  327.                 case kColorChanged:
  328.                     UseNewColor(myPicker);
  329.                     break;
  330.                 case kOkHit:
  331.                     UseNewColor(myPicker);
  332.                     DisposeColorPicker(myPicker);
  333.                     myPicker = nil;
  334.                     break;
  335.                 case kCancelHit:
  336.                     UseOriginalColor(myPicker);
  337.                     DisposeColorPicker(myPicker);
  338.                     myPicker = nil;
  339.                     break;
  340.                 case kNewPickerChosen:
  341.                     // You shouldn't care about this.
  342.                     break;
  343.                 case kApplItemHit:
  344.                     // Handle the item as you would for the Dialog Manager.
  345.                     HandleAppItem(pEvent.itemHit);
  346.                     break;
  347.             }
  348.         }
  349.     }
  350.     if(!handled) {
  351.         // The event hasn't been handled. Treat it as you would any normal
  352.         // Macintosh event (in this case we do nothing, and the calling
  353.         // routine handles it). If you have other dialogs, you need to call 
  354.         // DialogSelect. Remember, if the event is a mouseDown, you 
  355.         // already called FindWindow!
  356.     }
  357.     return handled;
  358. }
  359.  
  360. void UseNewColor(picker thePicker)
  361. {
  362.     PMColor newColor;
  363.     
  364.     GetPickerColor(thePicker, kNewColor, &newColor);
  365.     DoNewColorStuff(&newColor);
  366. }
  367.  
  368. void UseOriginalColor(picker thePicker)
  369. {
  370.     PMColor oldColor;
  371.     
  372.     GetPickerColor(thePicker, kOriginalColor, &oldColor);
  373.     DoNewColorStuff(&oldColor);
  374. }
  375.  
  376. void HandleAppItem(short itemHit)
  377. {
  378.     PMColor oldColor;
  379.  
  380.     switch(itemHit)
  381.     {
  382.         case kRevertButton:
  383.             GetPickerColor(myPicker, kOriginalColor, &oldColor);
  384.             SetPickerColor(myPicker, kNewColor, &oldColor);
  385.             DoNewColorStuff(&oldColor);
  386.             break;
  387.         
  388.         default:
  389.             break;
  390.     }
  391. }
  392.  
  393. // Handling the Edit Menu
  394. Boolean DoMenu(EventRecord *event)
  395. {
  396.     long             mChoice;
  397.     EditData         eData;
  398.     EditOperation     eOperation;
  399.  
  400.     // If picker is in front and current edit item is the picker's,
  401.     // set up the Edit menu how the picker wants it
  402.     if (FrontWindow() == myDialog &&
  403.             ((DialogPeek)myDialog)->editField + 1 > kMyLastItem) {
  404.         MenuState         mState;
  405.         MenuHandle        theMenu;
  406.         
  407.         GetPickerEditMenuState(myPicker, &mState);
  408.         theMenu = GetMenu(kMyEditMenuID);
  409.         if (mState.cutEnabled)
  410.             EnableItem(theMenu, kMyCutItem);
  411.         else
  412.             DisableItem(theMenu, kMyCutItem);
  413.         if (mState.copyEnabled)
  414.             EnableItem(theMenu, kMyCopyItem);
  415.         else
  416.             DisableItem(theMenu, kMyCopyItem);
  417.         if (mState.pasteEnabled)
  418.             EnableItem(theMenu, kMyPasteItem);
  419.         else
  420.             DisableItem(theMenu, kMyPasteItem);
  421.         if( mState.clearEnabled)
  422.             EnableItem(theMenu, kMyClearItem);
  423.         else
  424.             DisableItem(theMenu, kMyClearItem);
  425.         if (mState.undoEnabled)
  426.         {
  427.             SetItem(theMenu, kMyUndoItem, mState.undoString);
  428.             EnableItem(theMenu, kMyUndoItem);
  429.         }
  430.         else
  431.             DisableItem(theMenu, kMyUndoItem);
  432.     }
  433.  
  434.     // Give the event to the Menu Manager.
  435.     if (event->what == mouseDown)
  436.         mChoice = MenuSelect(event->where);
  437.     else
  438.         mChoice = MenuKey(event->message);
  439.     
  440.     // If not the edit menu, handle normally
  441.     if(HiWord(mChoice) != kMyEditMenuID)
  442.     {
  443.         HandleMenuChoice(mChoice);
  444.         return true;
  445.     }
  446.         
  447.     switch (LoWord(mChoice)) {
  448.         case kMyCutItem:
  449.             eOperation = kCut;
  450.             break;
  451.         case kMyCopyItem:
  452.             eOperation = kCopy;
  453.             break;
  454.         case kMyPasteItem:
  455.             eOperation = kPaste;
  456.             break;
  457.         case kMyClearItem:
  458.             eOperation = kClear;
  459.             break;
  460.         case kMyUndoItem:
  461.             eOperation = kUndo;
  462.             break;
  463.         default:
  464.             eOperation = -1;
  465.             break;
  466.     }
  467.     if (eOperation >= 0) {
  468.         eData.theEdit = eOperation;
  469.         DoPickerEdit(myPicker, &eData);
  470.         
  471.         // We're going to ignore the results here, and just assume the
  472.         // color changed
  473.         UseNewColor(myPicker);
  474.     }
  475.     HiliteMenu(0);
  476.     return true;
  477. }
  478.  
  479. void HandleMenuChoice(long mChoice)
  480. {
  481.     int DoMenus(long mstuff);
  482.     
  483.     // The menu choice isn't in the edit menu, let the shell handle it.
  484.     DoMenus(mChoice);
  485. }
  486.  
  487. void CloseAppPicker(void)
  488. {
  489.     
  490.     if(myPicker != nil)
  491.     {
  492.         DisposeColorPicker(myPicker);
  493.         myPicker = nil;
  494.     }
  495.     
  496.     if(myDialog != nil)
  497.     {
  498.         DisposeDialog(myDialog);
  499.         myPicker = nil;
  500.     }
  501. }
  502.  
  503. /////////////////////////////////////////////////////////////////////////
  504. //
  505. // Miscellaneous code
  506.  
  507. void SetPickerToColor(RGBColor *rgb);
  508. void SetPickerToColor(RGBColor *rgb) {
  509.     PMColor     aColor;
  510.  
  511.     aColor.color.rgb = *rgb;
  512.     aColor.profile = 0L;
  513.     SetPickerColor(myPicker, kOriginalColor, &aColor);
  514.     SetPickerColor(myPicker, kNewColor, &aColor);
  515. }
  516.  
  517. void GetCurrentColor(RGBColor *rgb);
  518. void GetCurrentColor(RGBColor *rgb) {
  519.     PMColor     aColor;
  520.  
  521.     GetPickerColor(myPicker, kNewColor, &aColor);
  522.     *rgb = aColor.color.rgb;
  523. }
  524.  
  525. void HandleError(void);
  526. void HandleError(void)
  527. {
  528.     SysBeep(0);
  529. }
  530.  
  531. void SetDestinationProfile(CMProfileHandle profile);
  532. void SetDestinationProfile(CMProfileHandle profile) {
  533.     if (SetPickerProfile(myPicker, profile) != noErr)
  534.         HandleError();
  535. }
  536.  
  537. void GetDestinationProfile(CMProfileHandle profile);
  538. void GetDestinationProfile(CMProfileHandle profile) {
  539.     if (GetPickerProfile(myPicker, &profile) != noErr)
  540.         HandleError();
  541. }
  542.  
  543. #include "Balloons.h"
  544. void DoBalloonHelp(void);
  545. void DoBalloonHelp(void) {
  546.     HelpItemInfo    helpInfo;
  547.     short            itemNo;
  548.     Point             where;
  549.     OSErr             err;
  550.  
  551.     GetMouse(&where);
  552.     itemNo = FindDItem(myDialog, where) + 1;
  553.  
  554.     // Go and get the color picker's help item.
  555.     helpInfo.options = 0;
  556.     helpInfo.tip.v = helpInfo.tip.h = 0;
  557.     SetRect(&helpInfo.altRect,0,0,0,0);
  558.     helpInfo.theProc = 0;
  559.     helpInfo.variant = 0;    
  560.     helpInfo.helpMessage.hmmHelpType = 0;
  561.     helpInfo.helpMessage.u.hmmPictHandle = 0L;
  562.     err = ExtractPickerHelpItem(myPicker, itemNo, 0, &helpInfo);
  563.  
  564.     // Show the balloon if we found one.
  565.     if (err == noErr) {
  566.         // If altRect is empty, we need to use the item's rectangle.
  567.         if (EmptyRect(&helpInfo.altRect)) {
  568.             short    iType;
  569.             Handle    iHandle;
  570.             
  571.             GetDItem(myDialog, itemNo, &iType, &iHandle, &helpInfo.altRect);
  572.         }
  573.                         
  574.         // Convert the tip to dialog coordinates.
  575.         helpInfo.tip.h += helpInfo.altRect.left;
  576.         helpInfo.tip.v += helpInfo.altRect.top;
  577.                         
  578.         // Convert the tip and altRect to global coordinates.
  579.         LocalToGlobal(&helpInfo.tip);
  580.         LocalToGlobal((Point *) &helpInfo.altRect.top);
  581.         LocalToGlobal((Point *) &helpInfo.altRect.bottom);
  582.  
  583.         // Finally, put the balloon up.
  584.         HMShowBalloon(&helpInfo.helpMessage, helpInfo.tip,
  585.             &helpInfo.altRect, 0L, helpInfo.theProc, helpInfo.variant,
  586.                 kHMRegularWindow);
  587.     }
  588. }
  589.